#include "gol1.h"
#include "dx.h"
#include "shd.h"
//#include "d3dx9core.h"

#pragma warning(disable:4996)	// sprintf is unsafe

LPD3DXMESH g_pDXTeapot;
LPDIRECT3D9 g_pD3D = NULL;
LPDIRECT3DDEVICE9 pDevice = NULL;
D3DPRESENT_PARAMETERS d3dpp; 


struct Vertex  *pVBuf;
struct sCamera g_Camera;

struct IDirect3DVertexBuffer9 *g_pGridVertexBuffer;
struct IDirect3DVertexBuffer9 *g_pVertexBuff;
struct ID3DXFont *g_pFont;
RECT FontPosition = {0, 0, 800, 800};


const DWORD VERTEX_FORMAT = ( D3DFVF_XYZ | D3DFVF_DIFFUSE );


double fDeltatime, fFPS;
double f10frmAvgFPS = 0;

#define LTRES (10000)
#define FLTRES ((float)LTRES)

#define __PI		(3.1415926535897932384626433832795f)
#define __HALFPI	(1.5707963267948966192313216916398f)
#define __TWOPI		(6.283185307179586476925286766559f)

double fTemp;

float fXAngle = 86.0f, fYAngle = 50.0f;
float fXOffs = 0.0f, fYOffs = 0.0f, fZOffs = 0.0f;

double g_fDeltaTime;




void DXInit( HWND hWnd )
{
	int i;
	D3DXFONT_DESC FontDesc = {16,
							  0,
							  400,
							  0,
							  FALSE,
							  DEFAULT_CHARSET,
							  OUT_TT_PRECIS,
							  CLIP_DEFAULT_PRECIS,
							  DEFAULT_PITCH,
							  "Arial"};
	ZeroMemory( &d3dpp, sizeof(d3dpp) );
	d3dpp.Windowed   = TRUE;
	d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
	d3dpp.BackBufferFormat = D3DFMT_UNKNOWN;
	d3dpp.EnableAutoDepthStencil = TRUE;
	d3dpp.AutoDepthStencilFormat = D3DFMT_D16;
	d3dpp.PresentationInterval = D3DPRESENT_INTERVAL_DEFAULT;//D3DPRESENT_INTERVAL_IMMEDIATE;

	//d3dpp.BackBufferWidth = 1280;
	//d3dpp.BackBufferHeight = 1024;
	

	g_pD3D = Direct3DCreate9(D3D_SDK_VERSION );
	IDirect3D9_CreateDevice( g_pD3D, D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, hWnd,
									D3DCREATE_HARDWARE_VERTEXPROCESSING,
									&d3dpp, &pDevice);

    InitShd( pDevice );

	D3DXCreateFontIndirect( pDevice, &FontDesc, &g_pFont );

    IDirect3DDevice9_SetRenderState(pDevice, D3DRS_CULLMODE, D3DCULL_NONE );
    IDirect3DDevice9_SetRenderState(pDevice, D3DRS_LIGHTING, FALSE );
	IDirect3DDevice9_SetRenderState(pDevice, D3DRS_ZENABLE, D3DZB_TRUE);

	IDirect3DDevice9_SetRenderState(pDevice, D3DRS_ALPHABLENDENABLE, TRUE);

//	IDirect3DDevice9_SetRenderState(pDevice, D3DRS_DIFFUSEMATERIALSOURCE, D3DMCS_COLOR1 );
//  Set the source blend state.
	IDirect3DDevice9_SetRenderState(pDevice, D3DRS_SRCBLEND, D3DBLEND_SRCALPHA);

// Set the destination blend state.

	IDirect3DDevice9_SetRenderState(pDevice, D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA);
	


	IDirect3DDevice9_CreateVertexBuffer( pDevice, GRIDLINES * 2 * 2 * sizeof( struct Vertex ), 0, VERTEX_FORMAT, D3DPOOL_DEFAULT, &g_pGridVertexBuffer, NULL );
    i = NUM_VERTICES;
	IDirect3DDevice9_CreateVertexBuffer( pDevice, NUM_VERTICES * sizeof( struct Vertex ), 0, VERTEX_FORMAT, D3DPOOL_DEFAULT, &g_pVertexBuff, NULL );

	IDirect3DVertexBuffer9_Lock( g_pGridVertexBuffer, 0, 0, (BYTE**)&pVBuf, 0 );

#define CELLBREADTH 1.0f
#define GRIDBREADTH (GRIDLINES * CELLBREADTH)
#define GRIDCOLTHICK	D3DCOLOR_RGBA( 255, 255, 255, 64 )
#define GRIDCOLTHIN		D3DCOLOR_RGBA( 255, 255, 255, 32 )
#define GRIDCOL			((i % 10) ? GRIDCOLTHIN : GRIDCOLTHICK)

	for( i = 0; i < GRIDLINES; i++ )
	{
		struct Vertex *currentVertex; 

		currentVertex = &pVBuf[i * 4 + 0];
		currentVertex->col	= GRIDCOL;
		currentVertex->y	= 0.0f;
		currentVertex->x	= -GRIDBREADTH * 0.5f;
		currentVertex->z	= i * CELLBREADTH - GRIDBREADTH  * 0.5f;

		currentVertex = &pVBuf[i * 4 + 1];
		currentVertex->col	= GRIDCOL;
		currentVertex->y	= 0.0f;
		currentVertex->x	= GRIDBREADTH * 0.5f;
		currentVertex->z	= i * CELLBREADTH - GRIDBREADTH  * 0.5f;

		currentVertex = &pVBuf[i * 4 + 2];
		currentVertex->col	= GRIDCOL;
		currentVertex->y	= 0.0f;
		currentVertex->x	= i * CELLBREADTH - GRIDBREADTH  * 0.5f;
		currentVertex->z	= -GRIDBREADTH * 0.5f;

		currentVertex = &pVBuf[i * 4 + 3];
		currentVertex->col	= GRIDCOL;
		currentVertex->y	= 0.0f;
		currentVertex->x	= i * CELLBREADTH - GRIDBREADTH  * 0.5f;
		currentVertex->z	= GRIDBREADTH * 0.5f;
	}

	IDirect3DVertexBuffer9_Unlock( g_pGridVertexBuffer );


	

	for( i = 0; i < LTRES; i++ )
	{
		//__sin[i] = sinf( (float)i * __TWOPI / FLTRES );
		//__cos[i] = cosf( (float)i * __TWOPI / FLTRES );
	}

	D3DXCreateTeapot( pDevice, &g_pDXTeapot, NULL );

	SetupMesh();
}

void UpdateStats()
{
	static DWORD tick = 0;
	const DWORD logFreq = 1000;
	LARGE_INTEGER nLocalCount;

	QueryPerformanceCounter( &nLocalCount );
	fDeltatime = (double)(nLocalCount.QuadPart - nCount.QuadPart)/nFreq.QuadPart;
    g_fDeltaTime = fDeltatime;

    nNumFrames++;
    nCount = nLocalCount;
	fFPS = 1.0f / fDeltatime;
    f10frmAvgFPS = ( 9.0f * f10frmAvgFPS + fFPS ) * 0.1f;

	fTemp = fAvgFPS * (double)( nNumFrames - 1.0f);
	fAvgFPS = (float)((fTemp + fFPS)/(double)nNumFrames);

	if( GetTickCount() - tick > logFreq )
	{
		char szOutPut[128] = "";
		tick = GetTickCount();
		sprintf( szOutPut, "frames: %d, FPS: %f\n", nNumFrames, f10frmAvgFPS );
		trace( szOutPut );
	}

}


void Loop(HWND hWnd)
{

#ifdef __GRAPH
	Redraw(hWnd);
#endif

    UpdateStats();

    Sleep( 1 );
}


void UpdateMesh2( float fLifeTime )
{
	int k, j, i, l;
	const float	DIST =  4.0f;

	IDirect3DVertexBuffer9_Lock( g_pVertexBuff, 0, 0, (BYTE**)&pVBuf, 0 );

	for( k = 0; k < NUM_LEAVES; k++ )
	{
		const D3DXVECTOR3 yAxis = { 0.0f, 1.0f, 0.0f };
		const D3DXVECTOR3 nullVect = { 0.0f, 0.0f, 0.0f };
		D3DXVECTOR3 v;
		D3DXMATRIX mx, my, m;
		float fPAngle = __TWOPI * (float)k / NUM_LEAVES;
		fPAngle = fPAngle;

		v.x = 10.0f*cosf( fPAngle );
		v.z = 10.0f*sinf( fPAngle );
		v.y = 0.f;

		D3DXMatrixRotationX( &mx, -__HALFPI * 0.2f * frand() );
		D3DXMatrixRotationY( &my, frand() * __TWOPI );
		D3DXMatrixMultiply( &m, &mx, &my );


		for( j = 0; j < RES_Y - 1; j++ )
		{
			for( i = 0; i < RES_X - 1; i++ )
			{
				for( l = 0; l < 6; l++ )
				{
					float xAbs = ( ( l == 0 || l == 3 || l == 5 ? (float)i / RES_X : (float)(i+1) / RES_X ) - 0.5f ) * 2.0f;
					float yAbs = ( ( l == 0 || l == 1 || l == 3 ? (float)j / RES_Y : (float)(j+1) / RES_Y ) - 0.5f ) * 2.0f;
					//float fTemp;

					int nCellIndex = 6 * ( k * ( RES_X - 1 ) * ( RES_Y - 1 ) + j * ( RES_X - 1 ) + i );
					struct Vertex *v = &pVBuf[nCellIndex];

					float fAX = xAbs * 1.0f;
					float fAY = yAbs;// * 10.0f;
					float fNTerm = ((yAbs + 1.0f)*0.5f) *3.0f;	//-1..1 -> 0..1
					float fTermY = 1.0f/(fNTerm*fNTerm + 1);
					float fTermX = 1.0f/( fAX * fAX + 1 );
					float fAngle = fTermX * fTermY * __PI * (1.0f +  sinf( fLifeTime * 2.0f ) )* 1.6f;


					v[l].col = 	( ( nCellIndex / 6 ) % 2 ) ? D3DCOLOR_XRGB( 0x00, 0xff, 0x00 ):D3DCOLOR_XRGB( 0x00, 0x00, 0xff ); //-1?

					v[l].x = xAbs * 30.0f;
					v[l].y = 0.0f;//sinf( fAngle )* 10.0f;
					v[l].z = yAbs * 30.0f;//( ( yAbs * 0.5f )+1.0f) * 30.0f + cosf( fAngle )* 10.0f;// ;

				}
			}
		}
	}

	IDirect3DVertexBuffer9_Unlock( g_pVertexBuff );
}



void SetupMesh( )
{
	int k, j, i, l;

	IDirect3DVertexBuffer9_Lock( g_pVertexBuff, 0, 0, (BYTE**)&pVBuf, 0 );

	for( k = 0; k < NUM_LEAVES; k++ )
	{
		for( j = 0; j < RES_Y - 1; j++ )
		{
			for( i = 0; i < RES_X - 1; i++ )
			{
				for( l = 0; l < 6; l++ )
				{
					float xAbs = ( ( l == 0 || l == 3 || l == 5 ? (float)i / RES_X : (float)(i+1) / RES_X ) - 0.5f ) * 2.0f;
					float yAbs = ( ( l == 0 || l == 1 || l == 3 ? (float)j / RES_Y : (float)(j+1) / RES_Y ) - 0.5f ) * 2.0f;

					int nCellIndex = 6 * ( k * ( RES_X - 1 ) * ( RES_Y - 1 ) + j * ( RES_X - 1 ) + i );
					struct Vertex *v = &pVBuf[nCellIndex];

					v[l].col = 	( ( nCellIndex / 6 ) % 2 ) ? D3DCOLOR_XRGB( 0x00, 0xff, 0x00 ):D3DCOLOR_XRGB( 0x00, 0x00, 0xff ); //-1?

					v[l].x = xAbs * 30.0f;
					v[l].y = 0.0f;
					v[l].z = yAbs * 30.0f;
				}
			}
		}
	}

	IDirect3DVertexBuffer9_Unlock( g_pVertexBuff );
}



void SetCamera()
{
	static D3DXVECTOR4 vEyePos =       { -27.38f, 28.67f, -47.96f };
	D3DXVECTOR4 vLookAt =       { 0.0f, 0.0f, 1.0f, 0.0f };
	D3DXVECTOR3 vInitUpVect =   { 0.0f, 1.0f, 0.0f };
	D3DXMATRIX mxEyeMat, myEyeMat, mEyeRes;
    D3DXMATRIX matView, matProj;
    D3DXVECTOR3 vXAxis = { 1.0f, 0.0f, 0.0f };
    D3DXVECTOR4 vYAxis = { 0.0f, 1.0f, 0.0f };
    D3DXVECTOR3 vZAxis = { 0.0f, 0.0f, 1.0f };
    D3DXVECTOR4 vOffs;
    D3DXVECTOR4 vOffsInit;

//orientation
    D3DXMatrixRotationX( &mxEyeMat, fXAngle * __HALFPI / 180.0f );      // rotation around x ( pitch )
	D3DXMatrixRotationY( &myEyeMat, fYAngle * __HALFPI / 180.0f );      // rotation around y ( yaw )
	D3DXMatrixMultiply( &mEyeRes, &mxEyeMat, &myEyeMat );               // composition 


//position
 
    vOffsInit.x = fXOffs;
    vOffsInit.y = fYOffs;
    vOffsInit.z = fZOffs;

    D3DXVec3Transform( &vOffs, (D3DXVECTOR3*)&vOffsInit, &mEyeRes );

    D3DXVec3Add( (D3DXVECTOR3*)&vEyePos, (D3DXVECTOR3*)&vEyePos, (D3DXVECTOR3*)&vOffs );                        // update the eye pos 
    D3DXVec3Add( (D3DXVECTOR3*)&vLookAt, (D3DXVECTOR3*)&vEyePos, (D3DXVECTOR3*)&mEyeRes.m[2] );


    fXOffs = 0.0f;
	fYOffs = 0.0f;
	fZOffs = 0.0f;

    D3DXMatrixLookAtLH( &matView, (D3DXVECTOR3*)(&vEyePos), (D3DXVECTOR3*)&vLookAt, (D3DXVECTOR3*)&vInitUpVect );	// construct view mat
	IDirect3DDevice9_SetTransform( pDevice, D3DTS_VIEW, &matView );                                                 // set view mat

	D3DXMatrixPerspectiveFovLH( &matProj, D3DX_PI/2.0f, 1.0f, 1.0f, 10000.0f );                                     // construct proj mat
	IDirect3DDevice9_SetTransform( pDevice, D3DTS_PROJECTION, &matProj );                                           // set proj mat

}   // SetCamera

void Redraw( HWND hWnd )
{
	char log[128] = "";

	static double fLifeTime = 0.0f;

    fLifeTime += fDeltatime;


	IDirect3DDevice9_Clear( pDevice, 0,  NULL, D3DCLEAR_TARGET|D3DCLEAR_ZBUFFER, D3DCOLOR_XRGB(0, 0, 0), 1.0f, 0);

	IDirect3DDevice9_BeginScene( pDevice );

    SetCamera();

	IDirect3DDevice9_SetFVF(pDevice, VERTEX_FORMAT );

	IDirect3DDevice9_SetRenderState(pDevice, D3DRS_ZENABLE, D3DZB_FALSE);
	IDirect3DDevice9_SetStreamSource(pDevice, 0, g_pGridVertexBuffer, 0, sizeof(struct Vertex));
	IDirect3DDevice9_DrawPrimitive(pDevice, D3DPT_LINELIST, 0 ,GRIDLINES * 2 );
	IDirect3DDevice9_SetRenderState(pDevice, D3DRS_ZENABLE, D3DZB_TRUE);

	IDirect3DDevice9_SetStreamSource(pDevice, 0, g_pVertexBuff, 0, sizeof(struct Vertex));
	IDirect3DDevice9_DrawPrimitive(pDevice, D3DPT_TRIANGLELIST, 0 ,NUM_TRIANGLES);


	sprintf( log, "frame: %d\nfps: %f", nNumFrames, f10frmAvgFPS );
	g_pFont->lpVtbl->DrawText( g_pFont, NULL, log, -1, &FontPosition, DT_TOP|DT_LEFT , 0xffffffff );

	g_pDXTeapot->lpVtbl->DrawSubset( g_pDXTeapot, 0 );

	IDirect3DDevice9_EndScene( pDevice );
	IDirect3DDevice9_Present( pDevice, NULL, NULL, NULL, NULL );
}


void Done(	HWND hWnd )
{

//	ppMesh->lpVtbl->Release( ppMesh );

	char szOutPut[128] = "";
	sprintf( szOutPut, "Num. Frames: %d, avg FPS: %f\n", nNumFrames, fAvgFPS );
	OutputDebugString( szOutPut );

	IDirect3DVertexBuffer9_Release( g_pGridVertexBuffer );
	IDirect3DVertexBuffer9_Release( g_pVertexBuff );
	g_pFont->lpVtbl->Release( g_pFont );
	IDirect3DDevice9_Release( pDevice );
	IDirect3D9_Release( g_pD3D );
}

